using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using EnvDTE80;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
//using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.VisualStudio.TeamFoundation.VersionControl;
using TeamReview.Services;
using TeamReview.Settings;
using System.Drawing;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace TeamReview.UI
{
    public partial class CreateWorkItemForm : Form
    {
        CodeSelection _codeSelection;

        private readonly DTE2 shellDTE;
        

        private readonly SettingsReaderWriter settings = new SettingsReaderWriter();
        private TeamSetting teamSetting;
        
        public CreateWorkItemForm(DTE2 shell, CodeSelection codeSelection)
        {
            InitializeComponent();
            shellDTE = shell;
            TFSService.TeamProjectChanged += new TeamProjectChangeEventHandler(TFSService_TeamProjectChanged);
           
            RestoreWindow();

            SetToolTips();

            _codeSelection = codeSelection;
            BindUsers();
            BindQueries();
            BindTextBoxes();
            BindPriority();
            
        }

        void TFSService_TeamProjectChanged()
        {
          Close();
        }


        private void UpdateWindowSettings()
        {
            WindowSetting ws = settings.Get(this.Name);
          
            if (ws == null)
            {
                ws = new WindowSetting();
                settings.Add(ws);               
            }
          
            ws.Name = this.Name;
            ws.Width = Width;
            ws.Height = Height;
            ws.WindowPoint = Location;
            ws.WindowState = WindowState;
        }

        private void UpdateTeamSettings()
        {

          TeamProject project = TFSService.GetTeamProject();
          TeamSetting ts = settings.Get(project);
          if (ts == null)
          {
            ts = new TeamSetting();
            ts.ArtifactUri = project.ArtifactUri.ToString();
            settings.Add(ts);
          }

          if (_cmbLinkedItem.SelectedItem != null)
            ts.ItemAssociated = ((BindingItemCodeReview)_cmbLinkedItem.SelectedItem).Id;
          else
            ts.ItemAssociated = 0;

          if (_cmbQuery.SelectedItem != null)
            ts.QueryGuid = ((BindingItemQuery)_cmbQuery.SelectedItem).QueryGuid;
          else
            ts.QueryGuid = Guid.Empty;

          if (_cmbAssignedUser.SelectedItem != null)
            ts.UserAssigned = _cmbAssignedUser.SelectedItem.ToString();
          else
            ts.UserAssigned = String.Empty;

          if (_cmbPriority.SelectedItem != null)
            ts.Priority = _cmbPriority.SelectedItem.ToString();
          else
            ts.Priority = String.Empty;

          ts.ChangeSet = textBoxChangeset.Text;
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);           
            Text = "Create Code Review Response";
        }


        private void RestoreWindow()
        {
            WindowSetting ws = settings.Get(this.Name);
            TeamProject project= TFSService.GetTeamProject();
            teamSetting = settings.Get(project);

            if (teamSetting == null)
            {
              teamSetting = new TeamSetting();
              teamSetting.ArtifactUri = project.ArtifactUri.ToString();
            }
  
          if (ws == null) 
                return;
            
            this.StartPosition = FormStartPosition.Manual;
            Width = ws.Width;
            Height = ws.Height;
            Location = ws.WindowPoint;
            WindowState = ws.WindowState;
        }

        #region Event Handlers

        
        private void btnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            _errorProvider1.Clear();
            bool validUser = ValidateAssignedUser();
            bool validTitle = ValidateTitle();
            bool validDescription = ValidateDescription();
            bool validPriority = ValidatePriority();
            if (!validUser || !validTitle || !validDescription || !validPriority)
                return;

            Save();
            Close();
        }


        private void cmbQuery_SelectedIndexChanged(object sender, EventArgs e)
        {
            BindLinkedItems();
        }

        private void cmbAssignedUser_Validating(object sender, CancelEventArgs e)
        {
            ValidateAssignedUser();
        }

        private void txtTitle_Validating(object sender, CancelEventArgs e)
        {
            ValidateTitle();
        }


        private void txtDescription_Validating(object sender, CancelEventArgs e)
        {
            ValidateDescription();
        }

        #endregion

        private WorkItemCollection GetAvailableQueryWorkItems(Guid queryGuid)
        {
            StoredQuery scQuery = TFSService.WorkItemStore.GetStoredQuery(queryGuid);
            string project = String.Format("'{0}'", TFSService.TeamProject.Name);
            string me = String.Format("'{0}'", TFSService.TeamProject.TeamFoundationServer.AuthenticatedUserName);
            string text = scQuery.QueryText.Replace("@project", project).Replace("@me", me);
            Query query = new Query(TFSService.WorkItemStore, text);
            query.SortFieldList.Clear();
            query.SortFieldList.Add("System.Id", SortType.Ascending);

            return TFSService.WorkItemStore.Query(query.QueryString);
        }


        private void 
          BindLinkedItems()
        {

            if (_cmbQuery.SelectedItem != null)
            {
                Guid queryGuid = ((BindingItemQuery) _cmbQuery.SelectedItem).QueryGuid;


                WorkItemCollection workItems = GetAvailableQueryWorkItems(queryGuid);

                if (workItems == null)
                    return;

                BindingList<BindingItemCodeReview> bl = new BindingList<BindingItemCodeReview>();
                BindingItemCodeReview selectedItem = null;  
              foreach (WorkItem workItem in workItems)
                {
                  BindingItemCodeReview bicr = new BindingItemCodeReview(workItem.Title, workItem.Id);
                    bl.Add(bicr);
                    if (workItem.Id == teamSetting.ItemAssociated)
                      selectedItem = bicr;
                }

                _cmbLinkedItem.DataSource = bl;

                if(selectedItem != null)
                  _cmbLinkedItem.SelectedIndex = _cmbLinkedItem.Items.IndexOf(selectedItem);
            }
        }


        private IDictionary<String, StoredQuery> GetAvailableTeamQueries()
        {
            StoredQueryCollection sq = TFSService.WorkItemStore.Projects[TFSService.TeamProject.Name].StoredQueries;
            SortedList<string, StoredQuery> sorted = new SortedList<string, StoredQuery>();
            foreach (StoredQuery q in sq)
                sorted.Add(q.Name, q);

            return sorted;
        }

        private void BindQueries()
        {
            IDictionary<String, StoredQuery> queries = GetAvailableTeamQueries();

            if (queries == null)
                return;

            BindingItemQuery selectedQuery = null; 
          BindingList<BindingItemQuery> bl = new BindingList<BindingItemQuery>();
            foreach (StoredQuery query in queries.Values)
            {
              BindingItemQuery biq = new BindingItemQuery(query.Name, query.QueryGuid);
                bl.Add(biq);
                if (query.QueryGuid.Equals(teamSetting.QueryGuid))
                  selectedQuery = biq;
            }

            _cmbQuery.DataSource = bl;
            _cmbQuery.DisplayMember = "Name";

            if (selectedQuery != null)
                _cmbQuery.SelectedIndex = _cmbQuery.Items.IndexOf(selectedQuery);
        }


        //private List<string> GetTeamUsers()
        //{
            //List<string> _users = new List<string>();

            //IGroupSecurityService gss = (IGroupSecurityService) tfs.GetService(typeof (IGroupSecurityService));
            //Identity[] projectGroups = gss.ListApplicationGroups(teamProject.ArtifactUri.ToString());

            //foreach (Identity projectGroup in projectGroups)
            //{
            //    if (!(projectGroup.DisplayName.Equals("Contributors") ||
            //          projectGroup.DisplayName.Equals("Project Administrators")))
            //        continue;

            //    Identity directMembers = gss.ReadIdentity(SearchFactor.Sid, projectGroup.Sid,
            //                                              QueryMembership.Expanded);
            //    foreach (string memberSid in directMembers.Members)
            //    {
            //        Identity member = gss.ReadIdentity(SearchFactor.Sid, memberSid, QueryMembership.None);

            //        if (!member.SecurityGroup && member.Type != IdentityType.WindowsGroup
            //            && member.Type != IdentityType.ApplicationGroup)
            //        {
            //            _users.Add(member.DisplayName);
            //        }
            //    }
            //}
            //_users.Sort();
            //return _users;
        //}

        private void BindUsers()
        {
            _cmbAssignedUser.Items.Clear();
            //TFSService.SetTeamProject(teamProject);
            List<string> users = TFSService.GetTeamUsers();
            if (users == null)
                return;

            _cmbAssignedUser.Items.AddRange(users.ToArray());
            if ((teamSetting != null) && (teamSetting.UserAssigned != null) && _cmbAssignedUser.Items.Count > 0)
              _cmbAssignedUser.SelectedIndex = _cmbAssignedUser.Items.IndexOf(teamSetting.UserAssigned);
        }

        private void BindPriority()
        {
          if (!TFSService.HasField(Constants.CODE_PRIORITY_FIELD))
          {
            _lblPriority.Font = new Font(_lblPriority.Font, FontStyle.Regular);
            _lblPriority.Refresh();
            Instrumentation.Trace(String.Format("To use the Priority drop down update the '{0}' work item type to the latest version from http://TeamReview.codeplex.com.", Constants.CODE_REVIEW_ITEM_TYPE));
            return;
          }

          AllowedValuesCollection ac = TFSService.PriorityAllowedValues;
          if (ac != null)
            _cmbPriority.DataSource = ac;

          if ((teamSetting != null) && !String.IsNullOrEmpty(teamSetting.Priority) && _cmbPriority.Items.Count > 0)
            _cmbPriority.SelectedIndex = _cmbPriority.Items.IndexOf(teamSetting.Priority);

        } 

        private void BindTextBoxes()
        {
            _txtSnippet.Text = _codeSelection.Snippet;
            if (String.IsNullOrEmpty(_codeSelection.CodeMember))
                _txtTitle.Text = String.Format("{0} {1}-{2}", _codeSelection.CodeFile, _codeSelection.StartLine, _codeSelection.EndLine);
            else
                _txtTitle.Text = String.Format("{0} {1}-{2}", _codeSelection.CodeMember, _codeSelection.StartLine, _codeSelection.EndLine);

            textBoxChangeset.Text = teamSetting.ChangeSet;
             
        }


        #region Data Validation

        private bool ValidateAssignedUser()
        {
            if (_cmbAssignedUser.SelectedItem != null)
                return true;

            if (!String.IsNullOrEmpty(_cmbAssignedUser.Text))
            {
                for (int i = 0; i < _cmbAssignedUser.Items.Count; i++)
                {
                    if (_cmbAssignedUser.Items[i].ToString().Equals(_cmbAssignedUser.Text))
                    {
                        _cmbAssignedUser.SelectedIndex = i;
                        return true;
                    }
                }
            }

            _errorProvider1.SetError(_lblAssignedUser, String.Format("You must assign a user to the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
            return false;
        }


        private bool ValidatePriority()
        {
          if (_cmbPriority.Items.Count == 0 || _cmbPriority.SelectedIndex >= 0)
            return true;

          _errorProvider1.SetError(_lblPriority, String.Format("You must supply a Priority for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
          return false;
        }

        private bool ValidateTitle()
        {
            if (!String.IsNullOrEmpty(_txtTitle.Text))
                return true;

            _errorProvider1.SetError(_lblTitle, String.Format("You must supply a Title for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
            return false;
        }


        private bool ValidateDescription()
        {
            if (!String.IsNullOrEmpty(_txtDescription.Text))
                return true;

            _errorProvider1.SetError(_lblDescription, String.Format("You must supply a Description for the new {0} work item", Constants.CODE_REVIEW_ITEM_TYPE));
            return false;
        }


        #endregion

        private WorkItem CreateWorkItem()
        {
            WorkItemType wiType = TFSService.WorkItemStore.Projects[TFSService.TeamProject.Name].WorkItemTypes[Constants.CODE_REVIEW_ITEM_TYPE];
            return wiType.NewWorkItem();
        }

        void Save()
        {
            WorkItem workItem = CreateWorkItem();

            try
            {
                workItem.Fields[Constants.CODE_DESCRIPTION_FIELD].Value = _txtDescription.Text;
                workItem.Fields[Constants.CODE_RESOLUTION_FIELD].Value = _txtResolution.Text;
                workItem.Fields[Constants.CODE_SNIPPET_FIELD].Value = _codeSelection.Snippet;
                workItem.Fields[Constants.CODE_FILE_FIELD].Value = _codeSelection.CodeFile;
                workItem.Fields[Constants.CODE_MEMBER_FIELD].Value = _codeSelection.CodeMember;
                workItem.Fields[Constants.CODE_PROJECT_FIELD].Value = _codeSelection.CodeProject;
                workItem.Fields[Constants.CODE_START_LINE_FIELD].Value = _codeSelection.StartLine.ToString();
                workItem.Fields[Constants.CODE_END_LINE_FIELD].Value = _codeSelection.EndLine.ToString();
                workItem.Fields[Constants.CODE_CLASS_FIELD].Value = _codeSelection.CodeClass;
                workItem.Fields[Constants.CODE_NAMESPACE_FIELD].Value = _codeSelection.Namespace;
                workItem.Fields[Constants.ASSIGNED_TO_FIELD].Value = _cmbAssignedUser.SelectedItem.ToString();
                workItem.Fields[Constants.TITLE_FIELD].Value = _txtTitle.Text;
                if(_cmbPriority.SelectedItem != null)
                  workItem.Fields[Constants.CODE_PRIORITY_FIELD].Value = _cmbPriority.SelectedItem;

                AddChangesetLink(workItem);

                AddRelation(workItem);

            
                  workItem.Save();
                 
                 string success = String.Format("{0} #{1} created and assigned to {2}",
                                                  Constants.CODE_REVIEW_ITEM_TYPE,
                                                  workItem.Id,
                                                  workItem.Fields[Constants.ASSIGNED_TO_FIELD].Value);
                 shellDTE.StatusBar.Text = success;
                 Instrumentation.Trace(success);

                 UpdateWindowSettings();
                 UpdateTeamSettings();
                 settings.Save();
               
            }
            catch (Exception e)
            {
               string message = "Field validation failed for the work item. Please file a defect with reproduceable steps at http://TeamReview.codeplex.com/";
                Instrumentation.Log(e);
               _errorProvider1.SetError(_lblBtnSave, message);
                            }
        }

        private void AddChangesetLink(WorkItem workItem)
        {
            try
            {

                if (!string.IsNullOrEmpty(textBoxChangeset.Text))
                {
                    // Add a source code file linkage for the changeset to the workitem
                    RegisteredLinkTypeCollection linkTypes = TFSService.WorkItemStore.RegisteredLinkTypes;
                    RegisteredLinkType linkType = linkTypes["Source Code File"];
                    if (linkType != null)
                    {

                        Changeset reviewChangeset = TFSService.VersionControlServer.GetChangeset(Int32.Parse(textBoxChangeset.Text));
                        if (reviewChangeset != null)
                        {
                            ExternalLink changeLink = new ExternalLink(linkType, reviewChangeset.ArtifactUri.AbsoluteUri);
                            workItem.Links.Add(changeLink);
                        }

                    }
                }
            }
            catch (Exception e)
            {
                Instrumentation.Log(e);
            }
        }


        private WorkItem GetWorkItem(int id)
        {
            return TFSService.WorkItemStore.GetWorkItem(id);
        }

        private void AddRelation(WorkItem workItem)
        {
            if (_cmbLinkedItem.SelectedIndex < 0)
                return;

            WorkItem relatedItem = GetWorkItem(((BindingItemCodeReview)_cmbLinkedItem.SelectedItem).Id);
            workItem.Links.Add(new RelatedLink(relatedItem.Id));

            if (relatedItem.Fields[Constants.AREA_FIELD] != null)
                workItem.Fields[Constants.AREA_FIELD].Value = relatedItem.Fields[Constants.AREA_FIELD].Value;

            if (relatedItem.Fields[Constants.ITERATION_FIELD] != null)
                workItem.Fields[Constants.ITERATION_FIELD].Value = relatedItem.Fields[Constants.ITERATION_FIELD].Value;

        }

        private void SetToolTips()
        {
            toolTip1.SetToolTip(_cmbQuery, "Select a Team Query to find a Work Item used for code review.");
            toolTip1.SetToolTip(_lblQueryFrom, toolTip1.GetToolTip(_cmbQuery));

            toolTip1.SetToolTip(_cmbLinkedItem, "Select a Work Item to associate with your new Code Review Response.");
            toolTip1.SetToolTip(_lblAssociateWith, toolTip1.GetToolTip(_cmbLinkedItem));

            toolTip1.SetToolTip(_cmbAssignedUser, "Select a TFS 'Contributor' or 'Project Administrator.'");
            toolTip1.SetToolTip(_lblAssignedUser, toolTip1.GetToolTip(_cmbAssignedUser));

            toolTip1.SetToolTip(_txtTitle, "Enter a title for your new Code Review Response.");
            toolTip1.SetToolTip(_lblTitle, toolTip1.GetToolTip(_txtTitle));

            toolTip1.SetToolTip(_txtDescription, "Enter why would you like the code to change.");
            toolTip1.SetToolTip(_lblDescription, toolTip1.GetToolTip(_txtDescription));

            toolTip1.SetToolTip(_txtResolution, "Enter how would you like the code to change.");
            toolTip1.SetToolTip(_lblResolution, toolTip1.GetToolTip(_txtResolution));
        }

        private void textBoxChangeset_Click(object sender, EventArgs e)
        {
            this.SendToBack();
            Changeset cs =  TFSService.VersionControlExt.FindChangeset();
            if (cs != null)
                textBoxChangeset.Text = cs.ChangesetId.ToString();

            this.BringToFront();
        }

        private void CreateWorkItemForm_Load(object sender, EventArgs e)
        {

        }
    }
}